﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Weather.Base;
using System.Globalization;
using System.Net;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using WeatherProviders;
using OpenWeatherMap.API;
using System.Web.Script.Serialization;

namespace OpenWeatherMap
{
    public class WeatherProvider : IWeatherProvider
    {
        const string _baseUrl = "http://openweathermap.org/data/2.1/";
        const decimal Kelvin = -273.15m;
        private readonly JavaScriptSerializer _jsonSerializer = new JavaScriptSerializer();
        #region GetLocations
        public List<LocationData> GetLocations(string query, CultureInfo culture)
        {
            return GetLocations(query, culture, TemperatureScale.Celsius);
        }
        public List<LocationData> GetLocations(string query, CultureInfo culture, TemperatureScale tempScale)
        {
            string responseJson = Helper.GetRequest(new Uri(String.Format(_baseUrl + "find/name?q={0}&lang={1}", query, culture.TwoLetterISOLanguageName)), Encoding.UTF8, 15000);
            var responseObj = _jsonSerializer.Deserialize<FindName>(responseJson);
            var apiData = responseObj.list.ToList();
            List<LocationData> data = new List<LocationData>();
            foreach (var ad in apiData)
                data.Add(new LocationData
                {
                    Code = ad.id.ToString(),
                    City = ad.name,
                    Country = ad.sys.country,
                    Skycode = GetWeatherCondition(ad.weather[0].id, ad.clouds.all, ad.weather[0].description, culture, Helper.IsDay(ad.date, ad.coord.lat, ad.coord.lon)).Key,
                    Temperature = GetTemperature(ad.main.temp),
                    Lat = (double)ad.coord.lat,
                    Lon = (double)ad.coord.lon
                });
            
            return data;
        }
        #endregion
        #region GetWeatherReport
        public WeatherData GetWeatherReport(CultureInfo culture, LocationData location, TemperatureScale tempScale, WindSpeedScale windSpeedScale, TimeSpan baseUtcOffset)
        {
            string responseJson = Helper.GetRequest(new Uri(_baseUrl + "weather/city/" + location.Code + "?lang=" + culture.TwoLetterISOLanguageName), Encoding.UTF8, 15000);
            var curent = _jsonSerializer.Deserialize<Location>(responseJson);
            WeatherData wd = new WeatherData
            {
                Location = location,
                Temperature = GetTemperature(curent.main.temp),
                Humidity = GetInt(curent.main.humidity),
                WindSpeed = GetInt(curent.wind.speed),
                Curent = new ForecastData
                {
                    HighTemperature = GetTemperature(curent.main.temp_max),
                    LowTemperature = GetTemperature(curent.main.temp_min),
                    SkyCode = GetWeatherCondition(curent.weather[0].id, curent.clouds.all, curent.weather[0].description, culture, Helper.IsDay(curent.date, curent.coord.lat, curent.coord.lon)).Key,
                    Text = GetWeatherCondition(curent.weather[0].id, curent.clouds.all, curent.weather[0].description, culture, Helper.IsDay(curent.date, curent.coord.lat, curent.coord.lon)).Value,
                    Url = curent.url
                }
            };
            wd.FeelsLike = GetInt(Helper.TeFelt((double)wd.Temperature, (double)wd.Humidity, (double)wd.WindSpeed));

            if (tempScale == TemperatureScale.Fahrenheit)
            {
                wd.Temperature = GetInt(Helper.TempF(wd.Temperature));
                wd.FeelsLike = GetInt(Helper.TempF(wd.FeelsLike));
                wd.Curent.HighTemperature = GetInt(Helper.TempF(wd.Curent.HighTemperature));
                wd.Curent.LowTemperature = GetInt(Helper.TempF(wd.Curent.LowTemperature));
            }

            WindSpeedScale wScale = WindSpeedScale.Ms;
            if (wScale != windSpeedScale)
                switch (windSpeedScale)
                {
                    case WindSpeedScale.Mph:
                        wd.WindSpeed = (int)Math.Round(WeatherConverter.WindSpeedConvertToMph(wd.WindSpeed, wScale), 0);
                        break;
                    case WindSpeedScale.Kmh:
                        wd.WindSpeed = (int)Math.Round(WeatherConverter.WindSpeedConvertToKmh(wd.WindSpeed, wScale), 0);
                        break;
                    case WindSpeedScale.Ms:
                        wd.WindSpeed = (int)Math.Round(WeatherConverter.WindSpeedConvertToMs(wd.WindSpeed, wScale), 0);
                        break;
                }

            responseJson = Helper.GetRequest(new Uri(_baseUrl + "forecast/city/" + location.Code + "?lang=" + culture.TwoLetterISOLanguageName), Encoding.UTF8, 15000);
            var fList = _jsonSerializer.Deserialize<ForecastList>(responseJson);

            foreach (var dayItems in fList.list.ToList().GroupBy(i => i.Date))
            {
                ForecastData data = new ForecastData();
                wd.ForecastList.Add(data);
                data.HighTemperature = GetTemperature(dayItems.Max(p => p.main.temp_max));
                data.LowTemperature = GetTemperature(dayItems.Min(p => p.main.temp_max));
                if (tempScale == TemperatureScale.Fahrenheit)
                {
                    data.HighTemperature = GetInt(Helper.TempF(data.HighTemperature));
                    data.LowTemperature = GetInt(Helper.TempF(data.LowTemperature));
                }

                data.Url = wd.Curent.Url;

                data.SkyCode = 1;
                data.Text = "";
            }
            return wd;
        }
        #endregion
        #region convert
        Int32 GetInt(decimal dec)
        {
            return Convert.ToInt32(Math.Round(dec));
        }
        Int32 GetInt(double dec)
        {
            return Convert.ToInt32(Math.Round(dec));
        }
        Int32 GetTemperature(decimal temp)
        {
            return GetInt(temp + Kelvin);
        }
        #endregion
        #region GetWeatherCondition
        KeyValuePair<Int32, String> GetWeatherCondition(Int32 Code, Int32 Cloud, String Text, CultureInfo culture, bool isDay)
        {
            bool ru = culture.TwoLetterISOLanguageName.ToLower() == "ru";
            Int32 code = 1;
            String text = Text;
            switch (Code)
            {
                case 200: //thunderstorm with light rain
                    text = !ru ? text : "Слабый дождь с грозой";
                    break;
                case 201: //thunderstorm with rain
                    text = !ru ? text : "Дождь с грозой";
                    break;
                case 202: //thunderstorm with heavy rain
                    text = !ru ? text : "Ливень с грозой";
                    break;
                case 210: //light thunderstorm
                    text = !ru ? text : "Гроза";
                    break;
                case 211: //thunderstorm
                    text = !ru ? text : "Гроза";
                    break;
                case 212: //heavy thunderstorm
                    text = !ru ? text : "Гроза";
                    break;
                case 221: //ragged thunderstorm
                    text = !ru ? text : "Гроза";
                    break;
                case 230: //thunderstorm with light drizzle
                    text = !ru ? text : "Слабая морось, гроза";
                    break;
                case 231: //thunderstorm with drizzle
                    text = !ru ? text : "Морось, гроза";
                    break;
                case 232: //thunderstorm with heavy drizzle
                    text = !ru ? text : "Морось, гроза";
                    break;
                /**************/
                case 300: //light intensity drizzle	
                    text = !ru ? text : "Слабая морось";
                    break;
                case 301: //drizzle	
                    text = !ru ? text : "Морось";
                    break;
                case 302: //heavy intensity drizzle	
                    text = !ru ? text : "Морось";
                    break;
                case 310: //light intensity drizzle rain
                    text = !ru ? text : "Слабый моросящий дождь";
                    break;
                case 311: //drizzle rain	
                    text = !ru ? text : "Моросящий дождь";
                    break;
                case 312: //heavy intensity drizzle rain	
                    text = !ru ? text : "Ливень";
                    break;
                case 321: //shower drizzle	
                    text = !ru ? text : "Ливень";
                    break;
                /**************/
                case 500: //light rain	
                    text = !ru ? text : "Слабый дождь";
                    break;
                case 501: //moderate rain
                    text = !ru ? text : "Слабый дождь";
                    break;
                case 502: //heavy intensity rain	
                    text = !ru ? text : "Ливень";
                    break;
                case 503: //very heavy rain	
                    text = !ru ? text : "Ливень";
                    break;
                case 504: //extreme rain	
                    text = !ru ? text : "Очень сильный ливень";
                    break;
                /**************/
                case 511: //freezing rain	
                    text = !ru ? text : "Переохлажденный дождь";
                    break;
                /**************/
                case 520: //light intensity shower rain	
                    text = !ru ? text : "Слабый дождь";
                    break;
                case 521: //shower rain	
                    text = !ru ? text : "Дождь";
                    break;
                case 522: //heavy intensity shower rain	
                    text = !ru ? text : "Слабый снег с дождем";
                    break;
                case 600: //light snow	
                    text = !ru ? text : "Слабый снег";
                    break;
                case 601: //snow	
                    text = !ru ? text : "Снег";
                    break;
                case 602: //heavy snow	
                    text = !ru ? text : "Снегопад";
                    break;
                case 611: //sleet	
                    text = !ru ? text : "Снег с дождем";
                    break;
                case 621: //shower snow	
                    text = !ru ? text : "Снегопад";
                    break;
                /**************/
                case 701: //mist	
                    text = !ru ? text : "Мгла";
                    break;
                case 711: //smoke	
                    text = !ru ? text : "Дым";
                    break;
                case 721: //haze	
                    text = !ru ? text : "Дымка";
                    break;
                case 731: //Sand/Dust Whirls	
                    text = !ru ? text : "Песочная буря";
                    break;
                case 741: //Fog	
                    text = !ru ? text : "Туман";
                    break;
                /**************/
                case 800: //sky is clear	  
                    text = !ru ? text : "Ясно";
                    break;
                case 801: //few clouds	
                    text = !ru ? text : "Небольшая облачность";
                    break;
                case 802: //scattered clouds	  
                    text = !ru ? text : "Облачно с прояснениями";
                    break;
                case 803: //broken clouds	  
                    text = !ru ? text : "Облачно";
                    break;
                case 804: //overcast clouds	  
                    text = !ru ? text : "Пасмурно";
                    break;
                /**************/
                case 900: //tornado
                    text = !ru ? text : "Торнадо";
                    break;
                case 901: //tropical storm
                    text = !ru ? text : "Тропический шторм";
                    break;
                case 902: //hurricane
                    text = !ru ? text : "Ураган";
                    break;
                case 903: //cold
                    text = !ru ? text : "Мороз";
                    break;
                case 904: //hot
                    text = !ru ? text : "Жара";
                    break;
                case 905: //windy
                    text = !ru ? text : "Ветрено";
                    break;
                case 906: //hail
                    text = !ru ? text : "Град";
                    break;
                default: break;
            }
            return new KeyValuePair<int,string>(code, text);
        }
        #endregion
    }
}